from zope.interface import implements

from twisted.internet import protocol, reactor, defer, ssl
from twisted.protocols import basic
from twisted.application import internet
from twisted.plugin import IPlugin
from twisted.python import components

from pyhnode import interfaces, errors, config

VERBOSE = True

class PyHMonProtocol(basic.NetstringReceiver):

    def __init__(self):
        self.auth = False
        
    def writeValue(self, value):
        if value != None:
            if VERBOSE:
                print value
            self.sendString(str(value))

    def stringReceived(self, msg):
        if VERBOSE:
            print msg
        d = self.factory.getAnswer(msg, self)
        d.addErrback(errors.catchError)
        d.addCallback(self.writeValue)

    def connectionMade(self):
        self.factory.newConnection(self, self.transport.getPeer().host)

    def connectionLost(self, reason):
        return self.factory.closeConnection(self)

class PyHMonFactoryFromService(protocol.ServerFactory):

    implements(interfaces.INetworkFactory)

    protocol = PyHMonProtocol

    def __init__(self, service):
        self.service = service

    def getAnswer(self, msg, p_inst):
        return self.service.getAnswer(msg, p_inst)

    def newConnection(self, p_inst, ipaddr):
        return self.service.newConnection(p_inst, ipaddr)

    def closeConnection(self, p_inst):
        return self.service.closeConnection(self)

class alertProto(basic.NetstringReceiver):
    
    def sendMsg(self, msg):
        self.sendString(msg)


class alertSender(object):

    implements(IPlugin, interfaces.IAlertPlugin)
    
    def __init__(self):
        self.name = "tcp"

    def configure(self):
        self.servers = {}
        for i in config.conf.get('general', 'servers').split(','):
            self.servers[i] = [config.conf.get(i, 'addr'),\
                config.conf.getint(i, 'alertport')]
        self.sslenabled = config.conf.get(self.name, 'ssl') == 'yes'
    
    def gotproto(self, p):
        p.sendMsg(self.msg)
        p.transport.loseConnection()

    def send(self, addr_and_port):
        c = protocol.ClientCreator(reactor, alertProto)
        if self.sslenabled:
            # read the configuration:
            myKey = config.conf.get(self.name, 'mysslcert')
            trustedCA = config.conf.get(self.name, 'casslcert')
            
            # read the certificate files:
            fd = open(myKey,'r')
            theCert = ssl.PrivateCertificate.loadPEM(fd.read())
            fd.close()
            fd = open(trustedCA,'r')
            theCA = ssl.Certificate.loadPEM(fd.read())
            fd.close()
            ctx = theCert.options(theCA)
            
            # set the SSL version to v3:
            ctx.method = ssl.SSL.SSLv3_METHOD
            
            # If True, verify certificates received from the peer and fail
            # the handshake if verification fails. Otherwise, allow anonymous
            # sessions and sessions with certificates which fail validation.
            ctx.verify = True

            # Depth in certificate chain down to which to verify.
            ctx.verifyDepth = 1
       
            # If True, do not allow anonymous sessions.
            ctx.requireCertification = True
       
            # If True, do not re-verify the certificate on session resumption.
            ctx.verifyOnce = True
       
            # If True, generate a new key whenever ephemeral DH parameters are
            # used to prevent small subgroup attacks.
            ctx.enableSingleUseKeys = True
       
            # If True, set a session ID on each context. This allows a shortened
            # handshake to be used when a known client reconnects.
            ctx.enableSessions = True
       
            # If True, enable various non-spec protocol fixes for broken
            # SSL implementations.
            ctx.fixBrokenPeers = False
            d = c.connectSSL(addr_and_port[0], addr_and_port[1], ctx)
        else:
            d = c.connectTCP(addr_and_port[0], addr_and_port[1])
        d.addCallback(self.gotproto)
        d.addErrback(errors.catchError)

    def sendAlert(self, msg):
        self.msg = msg
        for i in self.servers.keys():
            self.send(self.servers[i])

asp = alertSender()

class pluginConnector(object):

    implements(IPlugin, interfaces.INetPlugin)

    def __init__(self):
        self.name = "tcp"

    def getName(self):
        return self.name

    def makeService(self, parser):
        """This part is tricky, i want to use client verification (the client's
        certificate has to be signed by the trusted CA, but the
        twisted.internet.ssl.CertificateOptions doesn't work, and it isn't well
        documented. I had to use PrivateCertificate.options(), which makes what
        i need."""
        
        self.port = config.conf.getint(self.name, 'port')

        try:
            sslenabled = config.conf.get(self.name, 'ssl') == "yes"
        except:
            sslenabled = False
        if sslenabled:
            # read the configuration:
            myKey = config.conf.get(self.name, 'mysslcert')
            trustedCA = config.conf.get(self.name, 'casslcert')
            
            # read the certificate files:
            fd = open(myKey,'r')
            theCert = ssl.PrivateCertificate.loadPEM(fd.read())
            fd.close()
            fd = open(trustedCA,'r')
            theCA = ssl.Certificate.loadPEM(fd.read())
            fd.close()
            ctx = theCert.options(theCA)
            
            # set the SSL version to v3:
            ctx.method = ssl.SSL.SSLv3_METHOD
            
            # If True, verify certificates received from the peer and fail
            # the handshake if verification fails. Otherwise, allow anonymous
            # sessions and sessions with certificates which fail validation.
            ctx.verify = True

            # Depth in certificate chain down to which to verify.
            ctx.verifyDepth = 1
       
            # If True, do not allow anonymous sessions.
            ctx.requireCertification = True
       
            # If True, do not re-verify the certificate on session resumption.
            ctx.verifyOnce = True
       
            # If True, generate a new key whenever ephemeral DH parameters are
            # used to prevent small subgroup attacks.
            ctx.enableSingleUseKeys = True
       
            # If True, set a session ID on each context. This allows a shortened
            # handshake to be used when a known client reconnects.
            ctx.enableSessions = True
       
            # If True, enable various non-spec protocol fixes for broken
            # SSL implementations.
            ctx.fixBrokenPeers = False

            #make the service:
            s = internet.SSLServer(self.port,\
                interfaces.INetworkFactory(parser), ctx)
        else:
            self.port = config.conf.getint(self.name, 'port')
            s = internet.TCPServer(self.port,\
                interfaces.INetworkFactory(parser))
        return s

components.registerAdapter(PyHMonFactoryFromService,
            interfaces.IParserService,
            interfaces.INetworkFactory)
            
pc = pluginConnector()

